/*
 * Franco Catalano
 * Fernando Arias
 * Jonathan Quinn
 * 
 * CS 3240 Spring 2010
 * Coding Project
 * Part 1 
 */

import java.io.*;

public class Scanner
{
	/**
	 * Scan the given string for tokens
	 * @param s Tiny program string
	 * @return Tokens
	 * @throws Exception 
	 */
	public String scan(String s) throws Exception
	{
		return scanHelper(s, "");
	}

	/**
	 * Recursively pop off tokens from the Tiny program
	 * and add the correct token type to the return value
	 * @param s Tiny program
	 * @param ret Eventual return value (begins as "")
	 * @return Tokens
	 * @throws Exception 
	 */
	private String scanHelper(String s, String ret) throws Exception
	{
		if(s.length() == 0)
		{
			return ret;
		}
		//Equivalent to peek
		else if(s.toLowerCase().trim().startsWith("begin "))
		{
			if(ret.length() > 0)
				ret += " BEGIN";
			else
				ret += "BEGIN";
			
			//Equivalent to matchToken
			return scanHelper(s.substring(s.toLowerCase().indexOf("begin ")+5), ret);
		}
		else if(s.toLowerCase().trim().startsWith("end ") || 
				s.toLowerCase().trim().equals("end"))
		{
			ret += " END";
			return scanHelper(s.substring(s.toLowerCase().indexOf("end")+3), ret);
		}
		else if(s.trim().startsWith("("))
		{
			ret += " LEFTPAR";
			return scanHelper(s.substring(s.indexOf("(")+1), ret);
		}
		else if(s.trim().startsWith(")"))
		{
			ret += " RIGHTPAR";
			return scanHelper(s.substring(s.indexOf(")")+1), ret);
		}
		else if(s.trim().startsWith(":="))
		{
			ret += " ASSIGN";
			return scanHelper(s.substring(s.indexOf(":=")+2), ret);
		}
		else if(s.trim().startsWith(","))
		{
			ret += " COMMA";
			return scanHelper(s.substring(s.indexOf(",")+1), ret);
		}
		else if(s.trim().startsWith(";"))
		{
			ret += " SEMICOLON";
			return scanHelper(s.substring(s.indexOf(";")+1), ret);
		}
		else if(s.trim().startsWith("+"))
		{
			ret += " PLUS";
			return scanHelper(s.substring(s.indexOf("+")+1), ret);
		}
		else if(s.trim().startsWith("-"))
		{
			ret += " MINUS";
			return scanHelper(s.substring(s.indexOf("-")+1), ret);
		}
		else if(s.trim().startsWith("*"))
		{
			ret += " MULTIPLY";
			return scanHelper(s.substring(s.indexOf("*")+1), ret);
		}
		else if(s.trim().startsWith("%"))
		{
			ret += " MODULO";
			return scanHelper(s.substring(s.indexOf("%")+1), ret);
		}
		else if(s.toLowerCase().trim().startsWith(" print ") ||
				s.toLowerCase().startsWith(" print ") )
		{
			ret += " PRINT";
			return scanHelper(s.substring(s.toLowerCase().indexOf(" print")+6), ret);
		}
		else if(s.toLowerCase().trim().startsWith(" read ") ||
				s.toLowerCase().startsWith(" read ") )
		{
			ret += " READ";
			return scanHelper(s.substring(s.toLowerCase().indexOf(" read")+5), ret);
		}
		else
		{
			String curr = s.trim();
			if(curr.length() > 0)
			{
				if(Character.isDigit(curr.charAt(0)) && curr.charAt(0) != '0')
				{
					int i = 0;
					String digit = "";
					//Keep reading as long as there are digits
					while(Character.isDigit(curr.charAt(i)))
					{
						digit += curr.charAt(i);
						i++;
					}
					if(isNotOther(curr.charAt(i)))
						throw new Exception("Invalid Tiny Token");
					ret += " INTNUM";
					return scanHelper(s.substring(s.indexOf(digit)+i), ret);
				}
				else
				{
					int i = 0;
					String string = "";
					//Keep reading as long as the current character makes
					//sense
					if(curr.charAt(0) == '_' || Character.isLetter(curr.charAt(0)))
					{
						while(isNotOther(curr.charAt(i)) && isValidID(curr.charAt(i)) && i < 10)
						{
							string += curr.charAt(i);
							i++;
						}
						if(isNotOther(curr.charAt(i)))
							throw new Exception("Invalid Tiny Token");
						ret += " ID";
						return scanHelper(s.substring(s.indexOf(string)+i), ret);
					}
					else
					{
						throw new Exception("Invalid Tiny Token");
					}
				}
			}
			else
			{
				return ret;
			}
		}
	}

	/**
	 * Reads a file and returns the string text (in one line)
	 * @param filename The filename to read in
	 * @return
	 */
	public String readFile(String filename)
	{
		StringBuilder ret = new StringBuilder();

		try 
		{
			BufferedReader br =  new BufferedReader(new FileReader(new File(filename)));
			try 
			{
				String line = null;

				while (( line = br.readLine()) != null)
				{
					ret.append(line);
				}
			}
			finally 
			{
				br.close();
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}

		return ret.toString();
	}

	/**
	 * Opens a file (Tiny program), reads in the program, scans it
	 * for tokens, and outputs them to another file
	 * @param infile The filename of the Tiny program
	 * @param outfile The filename to output the tokens to
	 */
	public void scanAndPrint(String infile, String outfile)
	{
		try
		{
			String s = readFile(infile);
			String tokens = scan(s);
			FileWriter fw = new FileWriter(outfile);
			PrintWriter pw = new PrintWriter(fw);
			pw.println(tokens + " $");
			pw.close();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * A helper function to test a character to ensure it is not of any
	 * of the main token types
	 * @param c The character to test
	 * @return True if the character is not one of the types
	 */
	private boolean isNotOther(char c)
	{
		if(c != '(' && c != ')' && c != ':' && c != ',' && c != ';' &&
				c != '+' && c != '-' && c != '*' && c != '%' && c != ' ')
			return true;
		else
			return false;
	}
	
	/**
	 * Tests if the character is a valid character for an ID
	 * @param c The character to test
	 * @return Boolean, true if valid
	 */
	private boolean isValidID(char c)
	{
		if(Character.isLetter(c) || Character.isDigit(c) || c == '_')
			return true;
		else
			return false;
	}

	public static void main(String[] args)
	{
		Scanner scan = new Scanner();
		try
		{
			System.out.println(scan.scan("begin a := a + b; end"));
			System.out.println(scan.scan("begin beginner := printer print + ender * 123; end"));
			scan.scanAndPrint("test.ll1", "test.tok");
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

}
